/**
 * The contents of this file are subject to the license and copyright detailed in the LICENSE and NOTICE files at the root of the source tree and available online at
 * http://www.askosi.org/license.html
 */
package be.destin.skos.core;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;

import org.apache.log4j.Logger;

import be.destin.skos.search.LabelMatchType;
import be.destin.skos.search.SearchResult;
import be.destin.skos.search.SearchUtil;

/**
 * @author Christophe Dupriez
 * 
 */
@XmlType(propOrder = { "title", "description", "creator","contributor","namespace", "uri", "supraLanguage", "help","icon","display", "create", "internalNote", "defaultScheme", "member" })
public class CollectionScheme extends NoScheme {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/** log4j category */
	private static Logger log = Logger.getLogger(CollectionScheme.class);

    protected LinkedList<Concept> concepts = new LinkedList<Concept>();
	   
	private String defaultScheme = null;
	private transient ConceptScheme list = null;

	/**
	 * 
	 */
	public CollectionScheme() {
		super();
	}

	/**
	 * @param code
	 */
	public CollectionScheme(SkosManager skosManager,String code) {
		super(skosManager, code);
	}

	/**
	 * @param code
	 */
	public CollectionScheme(SkosManager skosManager,String code, ConceptScheme aDefaultList) {
		super(skosManager, code);
		this.list = aDefaultList;
		this.defaultScheme = aDefaultList.getAbout();
	}

	/*
	 * STUPID duplication made mandatory by XmlBinding...
	 */
	/**
	 * @return the displays
	 */
	@XmlElement
	public UrlList getDisplay() {
		return super.getDisplay();
	}

	@XmlElement
	public UrlList getCreate() {
		return creates;
	}

	/**
	 * @return the internalNote
	 */
	@XmlElement
	public String getInternalNote() {
		return super.getInternalNote();
	}

	/**
	 * @return the helps
	 */
	@XmlElement
	public UrlList getHelp() {
		return helps;
	}

	/**
	 * @return the icons
	 */
	@XmlElement
	public UrlList getIcon() {
		return super.getIcon();
	}

	/**
	 * @return the titles
	 */
	@XmlElement
	public TermList getTitle() {
		return super.getTitle();
	}

	/**
	 * @return the descriptions
	 */
	@XmlElement
	public TermList getDescription() {
		return super.getDescription();
	}

	/**
	 * @param language:
	 *            language of need help
	 * @return help URL in desired language
	 */
	public String getDescription(String language) {
		String description = this.conceptRepresentation.getScopeNote(language);
		if (description == null) {
			ConceptScheme aScheme = getMemberOf();
			if (aScheme == null) return null;
			return aScheme.getDescription(language);
		}
		return description;
	}

	/**
	 * @return the namespace
	 */
	@XmlElement(name="namespace")
	public String getNamespace() {
		return namespace;
	}

	/**
	 * @return the namespace
	 */
	@XmlElement(name="uri")
	public String getUri() {
		return uri;
	}

	/*
	 * END of DUPLICATION
	 */

	public String getAbout_About(String entryCode) {
		if (defaultScheme == null || defaultScheme.length() == 0) return entryCode;
		else {
			if (entryCode.indexOf(SchemeInterface.separator) >= 0)
				return entryCode;
			else return defaultScheme
				+ SchemeInterface.separator
				+ entryCode;
		}
	}

	/**
	 * @return the complete prefixed code of an entry (after applying
	 *         redirections)
	 */
	public String getAbout_Concept(String fullHandle) {
		Concept anEntry = findAbout_Concept(fullHandle);
		return anEntry.getScheme_About();
	}

	/**
	 * @return the concepts
	 */
	@XmlTransient
	public synchronized LinkedList<Concept> getConcept() {
		if (this.noConcepts) return null;
		return concepts;
	}

	@XmlTransient
	public synchronized LinkedList<Concept> getAllConcepts() {
		return concepts;
	}

	/**
	 * @return the concepts for an handle (redirected or not). Null if none
	 */
	public synchronized LinkedList<Concept> getConcept(Collection<String> fullHandles) {
		if (fullHandles == null) return null;
		Concept anEntry;
		LinkedList<Concept> result = new LinkedList<Concept>();
		for (String anHandle : fullHandles) {
			anEntry = this.getConcept(anHandle);
			if (anEntry != null) result.add(anEntry);
		}
		return result;
	}

	/**
	 * @return the concepts for an handle (redirected or not). Null if none
	 */
	public synchronized Concept getConcept(String fullHandle) {
		SkosManager skosManager = this.getManagerInstance();
		if (skosManager == null) return null;
		if (fullHandle == null) return null;
		Concept aConcept = skosManager.getScheme_Concept(fullHandle, defaultScheme);
		if (aConcept == null) return null;
		LinkedList<CollectionScheme> collection = aConcept.getCollectionScheme();
		if (collection!=null) {
			if (collection.contains(this)) return aConcept;
		}
		return null;
	}

	/**
	 * @param language
	 * @return the best name for current language
	 */
	public String getDisplay(String language) {
		String display = this.conceptRepresentation.getUrl(language);
		if (display == null) {
			ConceptScheme aScheme = getMemberOf();
			if (aScheme == null) return null;
			return aScheme.getDisplay(language);
		}
		return display;
	}

	/**
	 * @param language
	 * @return the best name for current language
	 */
	public String getCreate(String language) {
		if (creates == null)  {
			ConceptScheme aScheme = getMemberOf();
			if (aScheme == null) return null;
			return aScheme.getCreate(language);
		}
		return creates.getLing(language);
	}

	/**
	 * @param language:
	 *            language of need help
	 * @return help URL in desired language
	 */
	public String getHelp(String language) {
		if (helps.getLing(language) == null) {
			ConceptScheme aScheme = getMemberOf();
			if (aScheme == null) return null;
			return aScheme.getHelp(language);
		}
		return helps.getLing(language);
	}

	/**
	 * @param language
	 * @return the best name for current language
	 */
	public String getIcon(String language) {
		String icon = this.conceptRepresentation.getIcon(language);
		if (icon == null) {
			ConceptScheme aScheme = getMemberOf();
			if (aScheme == null) return null;
			return aScheme.getIcon(language);
		}
		return icon;
	}

	/**
	 * @param language:
	 *            language of need help
	 * @return help URL in desired language
	 */
	public String getTitle(String language) {
		String title = this.conceptRepresentation.getPrefLabel(language, false);
		if (title == null) {
			ConceptScheme aScheme = getMemberOf();
			if (aScheme == null) return null;
			return aScheme.getTitle(language);
		}
		return title;
	}

	/**
	 * @param entry
	 *            to put amongst concepts
	 */
	public synchronized Concept putConcept(Concept entry) {
		if ( ! concepts.contains(entry)) {
			concepts.add(entry);
			entry.putCollectionScheme(this);
		}
		return entry;
	}

	/**
	 * @param conceptMap
	 *            the concepts to set
	 */
	public synchronized void setConcept(List<Concept> someEntries) {
		if (concepts != null) {
			LinkedList<Concept> toBeRemoved = new LinkedList<Concept>();
			for ( Concept aConcept : concepts ) {
				if ( ! someEntries.contains(aConcept)) {
					toBeRemoved.add(aConcept);
					LinkedList<CollectionScheme> conceptColls = aConcept.getCollectionScheme();
					if (conceptColls != null) {
						conceptColls.remove(this);							
					}
					
				}
			}
			concepts.removeAll(toBeRemoved);
		} else {
			concepts = new LinkedList<Concept>();
		}
		for (Concept anEntry : someEntries) {
			putConcept(anEntry);
		}
	}

    /*
     * How many concepts in this scheme?
     */
	@XmlTransient
	public int getSize() {
		return concepts.size();
	}

	public ArrayList<SearchResult> labelMatch(String prefix, String userLanguage, boolean strictLanguage, LabelMatchType matchType) {
		return SearchUtil.labelMatch( this.getManagerInstance(), prefix, userLanguage, strictLanguage, matchType, concepts);
	}

	/**
	 * @return the defaultScheme
	 */
	@XmlElement
	public String getDefaultScheme() {
		return defaultScheme;
	}

	/**
	 * @param defaultScheme the defaultScheme to set
	 */
	public void setDefaultScheme(String defaultScheme) {
		if (defaultScheme == null) this.defaultScheme = null;
		else this.defaultScheme = SkosUtil.normalizeAbout(defaultScheme);
		this.list = null;
	}
	
	@XmlTransient
	private ConceptScheme getMemberOf() {
		SkosManager skosManager = this.getManagerInstance();
		if (skosManager == null) return null;
		if (this.list != null) return list;
		SchemeInterface aList = skosManager.getScheme(defaultScheme);
		if (aList instanceof ConceptScheme) {
			list = (ConceptScheme) aList;
			return list;
		}
		else {
			String message = "Collection "+this.getAbout()+" has a bad default scheme: "+defaultScheme;
			log.error(message);
			this.getErrors().add(message);
			return null;
		}
	}

	/**
	 * @return the collection members (always an ordered list)
	 */
	@XmlElement
	public LinkedList<String> getMember() {
		LinkedList<String> conceptAbout = new LinkedList<String>();
		for (Concept aConc : concepts) {
			conceptAbout.add(aConc.getAbout());
		}
		return conceptAbout;
	}
	/**
	 * @return the collection members (always an ordered list)
	 */
	@XmlTransient
	public LinkedList<String> getMemberList() {
		return getMember();
	}
	

	/**
	 * @param redirection:
	 *            the redirection to add in the set
	 */
	public void putMember(String about) {
		if (about == null) return;
		SkosManager skosManager = this.getManagerInstance();
		if (skosManager == null) return;
		about = SkosUtil.normalizeAbout(about);
		Concept aMember = skosManager.getScheme_Concept(about);
		if (aMember != null) putConcept(aMember);
	}

	/**
	 * @param collection the collection to set
	 */
	public void setMember(LinkedList<String> memberAbout) {
		for (String aConcept : memberAbout) {
			putMember(aConcept);
		}
	}

	@XmlElement
	public LinkedList<String> getCreator() {
		return creators;
	}

	public void setCreator(LinkedList<String> creators) {
		this.creators = creators;
	}

	@XmlElement
	public LinkedList<String> getContributor() {
		return contributors;
	}

	public void setContributor(LinkedList<String> contributors) {
		this.contributors = contributors;
	}

	/**
	 * @return the supraLanguage
	 */
	@XmlElement
	public LinkedList<String> getSupraLanguage() {
		if (this.supraLanguage == null) this.supraLanguage = new LinkedList<String>();
		return this.supraLanguage;
	}

	public boolean equals(Object o) {
		return super.equals(o);
	}

}
